﻿# importing required modules
import os, time, shutil, subprocess, re


class log_folder_dirs:
    class log_folder_dir:
        '类:生产log每日文件夹'

        path_7za = r'.\7z2201-extra\7za.exe'

        def __init__(self, root: str, relative_path: list[str], save_level: int):
            # C:\AA1 Result=root / BYD X1F=level 1/ 2023=level 2。。。
            # C:\AA1 Result\BYD X1F\2023\08\11
            dash = '_'
            slash = '\\'
            self.new_name = dash.join(relative_path)
            self.path = root + slash + slash.join(relative_path)
            self.target_path = root + slash + slash.join(relative_path[:save_level])
            self.m_time = os.stat(self.path).st_mtime

        def clean_folder(
            self,
            time_limit: int,
            match_str: list[str],
            mark_level: int,
            re_ph: list[str] = [],
        ) -> int:
            """Clean the folder with specify condition

            Args:
                time_limit (int): Time limit Second.
                match_str (list[str]): Match string
                mark_level (int): the mark in which level
                re_ph (list[str], optional): should to [].

            Returns:
                int: _description_
            """
            mark = '-cleaned'
            slash = '\\'
            cleaned = 0
            all_cleaned = True
            current_time = int(time.time())
            match_level = len(match_str)
            if match_level <= 0 or mark_level > match_level:
                return -1  # Input Error
            ab_ph = slash.join([self.path, *re_ph])
            if mark_level == 0:
                if ab_ph[-len(mark) :] == mark:
                    return 0  # Skipped Archive cause with the name
            for file_name in os.listdir(ab_ph):
                file_abph = os.path.join(ab_ph, file_name)
                matYN = re.search(match_str[0], file_name)
                if matYN != None:
                    if match_level == 1:
                        file_time = int(os.stat(file_abph).st_mtime)
                        if current_time - file_time < time_limit:
                            all_cleaned = False
                            continue
                        if os.path.isfile(file_abph):
                            os.remove(file_abph)
                        else:
                            shutil.rmtree(file_abph)
                        cleaned += 1
                    else:
                        if os.path.isdir(file_abph):
                            returned = self.clean_folder(
                                time_limit,
                                match_str[1:],
                                mark_level - 1,
                                [*re_ph, file_name],
                            )
                            if returned >= 0:
                                cleaned += returned
            if mark_level == 0 and all_cleaned:
                level = len(re_ph)
                os.rename(ab_ph, ab_ph + mark)
                if level == 0:
                    self.path = ab_ph + mark
            return cleaned

        def sevenzip(self, remian_time: int) -> int:
            """Archive file with 7za.exe

            Args:
                remian_time (int): Time to Archive

            Returns:
                int: 0 -- File Archived Skipped \n
                int: 1 -- Archived successfuly \n
                int:-1 -- Archived with some error \n
                int:-2 -- Another archiving process is working!
            """
            now = time.time()
            if (now - self.m_time) > remian_time:
                timemark = "-" + time.strftime("%y%m%d%H%M%S", time.localtime(now))
                archive_list_txt = self.path + "\\Archive Lists_By_Yingestech.txt"
                if os.path.exists(archive_list_txt):
                    try:
                        os.remove(archive_list_txt)
                    except:
                        return -2
                content = (
                    self.path
                    + ' ==> '
                    + self.new_name
                    + timemark
                    + '.7z - By_Yingestech\r\nList:\n'
                    + '\n'.join(os.listdir(self.path))
                )
                with open(archive_list_txt, 'a+', encoding='utf-8') as fs:
                    fs.write(content)
                alt = open(archive_list_txt)
                newpath_newname = (
                    self.target_path + "\\" + self.new_name + timemark + ".7z"
                )
                new_old_Path_Name = "\"" + newpath_newname + "\" \"" + self.path + "\""
                path_7za = "\"" + self.path_7za + "\""
                cmd = path_7za + " a -bso2 -bse2 -bsp2 -mmt2 " + new_old_Path_Name
                ret = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
                for line in iter(ret.stdout.readline, 'b'):
                    line = line.rstrip().decode('utf8')
                    if (subprocess.Popen.poll(ret) is not None) and line == "":
                        break
                ret.stdout.close()
                alt.close()
                if ret.returncode == 0:
                    print(newpath_newname + " archived!\n")
                    shutil.rmtree(self.path)  # delete zipped file
                    return 1
                else:
                    return -1
            else:
                return 0

    def __init__(self, root_dir: str, list_level=4, save_level=2):
        self.list_level = list_level
        self.save_level = save_level
        if self.list_level < self.save_level:
            print("Error:Parameter intut with issue, required:list_level>=save_level")
            return
        self.root_dir = root_dir
        self.part_folder_list = self.__get_all_folder()

    def __get_all_folder(
        self, working_dir="", rel_path: list[str] = [], i=1
    ) -> list[log_folder_dir]:
        """Traverse all folder in the path, --Private methord--

        Args:
            working_dir (str, optional): root folder input. Defaults to "".
            rel_path (list[str], optional): Should be [].
            i (int, optional): Should be 1.

        Returns:
            list[log_folder_dir]: _description_
        """
        empty_lis: list[str] = []
        if i > self.list_level:
            return empty_lis
        folder_lis: list[self.log_folder_dir] = []
        if i == 1:
            folders = os.listdir(self.root_dir)
            working_dir = self.root_dir
        else:
            folders = os.listdir(working_dir)
        for folder in folders:
            re_path_add = rel_path + [folder]
            root_add_folder = os.path.join(working_dir, folder)
            if os.path.isfile(root_add_folder):
                continue
            if i < self.list_level:
                folder_lis = folder_lis + self.__get_all_folder(
                    root_add_folder, re_path_add, i + 1
                )
            else:
                folder_lis.append(
                    self.log_folder_dir(self.root_dir, re_path_add, self.save_level)
                )
        return folder_lis

    def clean_folders(self, remain_time: int, match: list[str], mark_level: int):
        """Clean folder with specify condition

        Args:
            remain_time (int): Clean the file last for how long time
            match (list[str]): Match string
            mark_level (int): Mark '-cleaned'in which folder

        Returns:
            _type_: how many folder or file that cleaned
        """
        cleaned = 0
        for part_folder in self.part_folder_list:
            returned = part_folder.clean_folder(remain_time, match, mark_level)
            if returned > 0:
                cleaned += returned
        return cleaned

    def sevenzips(self, remain_time, zips_in_one_time=5) -> int:
        """Archive Logs with 7za.exe

        Args:
            remain_time (_type_): _description_
            zips_in_one_time (int, optional): how many logs should be archived in this time. Defaults to 5.

        Returns:
            int: Archived QTY Out\n
            if count = -1 Zipped with Error\n
            if Count = -2 Zipped Terminated Because Other process is Running
        """
        count = 0
        for folder in self.part_folder_list:
            if count > zips_in_one_time - 1:
                break
            ar_result = folder.sevenzip(remain_time)
            if ar_result == 1:
                count += 1
            elif ar_result == 0:
                pass
            elif ar_result == -1:
                count = -1  # error 1
                break
            elif ar_result == -2:
                count = -2  # error 2
                break
        return count


def demomain(log_root_dir, clean_remain, logs_Remain, zips_in_one_time: int = 5):
    if zips_in_one_time < 0:
        print("Error:'zips_in_one_time'<0,Please Reset")
        return
    # Get the list folder
    folder_lists = log_folder_dirs(log_root_dir, 4, 2)
    # Clean Sweep folder
    print("start to clean folder...")
    cleaned = folder_lists.clean_folders(clean_remain, ['', 'Sweep'], 0)
    if cleaned > 0:
        print(
            "Cleaned %s parts folder with Sweep remain for more then %.2f mins"
            % (cleaned, (clean_remain / 60))
        )
    elif cleaned == 0:
        print("No sweep folder to delete")
    if zips_in_one_time == 0 and cleaned >= 0:
        print("zips_in_one_time = 0,No zip task!")
    elif cleaned >= 0:
        archived = folder_lists.sevenzips(logs_Remain, zips_in_one_time)
        if archived == -1:
            print("Error occured,archice process stopped")
        elif archived == -2:
            print("Another archiving process is working! Please try it later\n")
        elif archived == 0:
            print("No file ready to archive!")
        else:
            print("%s files archived successfully!" % archived)


if __name__ == "__main__":
    log_root_dir = r'C:\Users\tding1\Desktop\TestResults'
    demomain(log_root_dir, 5, 10, 1)
